package com.df.gateway.handler;

import com.alibaba.fastjson2.JSON;
import com.df.base.entity.ClientMsg;
import com.df.base.other.R;
import com.df.base.other.RpcReq;
import com.df.base.other.SpringContextHelper;
import com.df.gateway.config.ServiceMappingConfig;
import com.df.gateway.entity.Client;
import com.df.gateway.entity.RegisterTokenReq;
import com.df.gateway.magager.ClientManager;
import com.df.gateway.utils.DubboUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@Slf4j
public class GatewayTcpHanlder extends SimpleChannelInboundHandler<ByteBuf> {


    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf o) throws Exception {

        //协议格式： 魔术(2字节，模仿Java魔术，我们起个fafa)|长度(4字节)|服务id(2字节)|请求路径hash(4字节)|version|消息体
        //协议格式： 魔术(2字节，模仿Java魔术，我们起个fafa)|长度(4字节)|服务id(2字节)|功能id(2字节)|业务方法id(2字节)|version|消息体
        //version  方案1（1.0.0  高位占8位，中位占8位，低位占16位）  方案2(自然数自增) 这两种方案换算成int只都一样，能占全部int值
        // Account/User/Login/1.0.0
        o.skipBytes(6);
        int serverId = o.readUnsignedShort();
        int pathId = o.readInt();
        int version = o.readInt();
        String versionStr = toVersionStr(version);
        byte[] body = new byte[o.readableBytes()];
        o.readBytes(body);

        //长连接第一个消息必须是注册token,也可以配置多个无需认证的请求
        Client client = ClientManager.Instance().getClient(channelHandlerContext.channel());
        if(serverId==0 && pathId==0){
            //TODO 检查消息体token内容，并注册到关联的client,token或playerId设置的channel的atribute里
            String s = java.lang.String.valueOf(body);
            RegisterTokenReq registerTokenReq = JSON.parseObject(s, RegisterTokenReq.class);
            //TODO 检查token的合法性,不合法则断开连接
            client.setToken(registerTokenReq.getToken());
            client.setAuthed(true);
            channelHandlerContext.channel().attr(ClientManager.tokenKey).set(registerTokenReq.getToken());
            R r = new R();
            r.setCode(200);
//            r.setMsgid();
            channelHandlerContext.writeAndFlush(Unpooled.wrappedBuffer(JSON.toJSONString(r).getBytes(StandardCharsets.UTF_8)));

        }else {
            //其他所有消息都需要认证后才能访问
            if(client==null){//未关联client，直接关闭连接
                channelHandlerContext.close();
                return;
            }
            //还未token认证，其他消息不放行
//            if(client.isAuthed()){
            //测试用，不用认证
            if(!client.isAuthed()){

                RpcReq req = new RpcReq();
                req.setBody(body);
                //TODO 从channel的atrribute里获取
//                req.setPlayerId();
//                req.setPath();

//                beforeSend();//发送前的检查：消息请求统计(消息请求次数)，流量统计，白名单检查等
                //消息放入客户端的消息队列,TODO MessageWrapper 实例可以放入对象池，因为会频繁创建此对象
                ServiceMappingConfig bean = SpringContextHelper.getBean(ServiceMappingConfig.class);
                if(bean==null){
                    log.error("未找到服务映射表！");
                    return;
                }
                String serverIdStr = bean.getMapping().get(serverId);
//                client.sendMsg.put(new MessageWrapper(serverId+"",versionStr,req,client.getToken()));
                ClientManager.Instance().handleMsg(client,new ClientMsg(serverIdStr,versionStr,req,client.getToken(),true));
//                ClientManager.Instance().handleMsg(client,new ClientMsg("Scenes","0.0.1",req,client.getToken(),true));
            }else {
                //TODO 发送消息不应该在read里执行，因为这个read方法可能会多次调用，最好将发送的消息放到一个队列，让队列依次发送消息
                channelHandlerContext.pipeline().writeAndFlush(Unpooled.wrappedBuffer("未认证".getBytes()));
            }
        }

    }


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        //绑定一个Client
        //todo 一个用户建立多个TCP连接的检查
        Client client = new Client();
        client.setChannel(ctx.channel());
        ClientManager.Instance().setClient(client);
        //建立连接10秒检查是否已经通过认证了，没有则关闭连接
        ctx.channel().eventLoop().schedule(()->{
            if(client.isAuthed()){
                return;
            }
            ClientManager.Instance().removeClient(client.getChannel());
            client.getChannel().writeAndFlush(Unpooled.wrappedBuffer("长时间未认证，断开连接！".getBytes())).addListener(ChannelFutureListener.CLOSE);
            log.warn("连接超过30秒未认证，关闭连接！");
        },30, TimeUnit.SECONDS);

        super.channelActive(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        //在连接管理器中移除此连接，关闭连接
        super.channelUnregistered(ctx);
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("发生异常");
        //TODO 在连接管理器中移除此连接，关闭连接
        super.exceptionCaught(ctx, cause);
    }

    private String toVersionStr(int version) {
        int littileVersion = version & 0xffff;
        int midVersion = (version>>16) & 0xffff;
        int highVersion = (version>>24) & 0xffff;
        return highVersion+"."+midVersion+"."+littileVersion;
    }


}
